package com.ylx.excel.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.URLUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.ylx.core.utils.ExceptionUtil;
import com.ylx.excel.excel.handler.CustomCellWeightWeightConfig;
import com.ylx.excel.excel.handler.ExcelCellWriteHandler;
import com.ylx.excel.excel.handler.ExcelListener;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;

/**
 * Description: <br/>
 * date: 2022/8/23 16:34<br/>
 * Excel工具类
 * @author yanglixin2<br />
 */
@Slf4j
public final class ExcelUtils {

    /**
     * 导出全部
     */
    public static final int ALL = 1;
    /**
     * 导出选中
     */
    public static final int CHOOSE = 2;
    /**
     * 表格样式
     */
    public static final HorizontalCellStyleStrategy STYLE = getStyle();
    /**
     * Excel上传到阿里云的访问地址
     * bucketName endpoint fileName
     */
    public static String EXCEL_URL = "https://%s.%s/%s";
    /**
     * Excel的文件名
     * 模块名 日期 唯一标识
     */
    public static final String EXCEL_FILE_NAME = "excel/%s/%s/%s.xlsx";

    private ExcelUtils() {}

    /**
     * 获取excel导出的文件名
     * @author yanglixin
     */
    public static String getDownLandName(String fileShowName) {
        return fileShowName + DateUtil.format(new Date(),"_yyyy-MM-dd_HHmmss");
    }

    /**
     * 导出Excel生产本地文件
     * @param data 导出的数据
     * @param excelWriter 写excel的writer
     * @param pageNum 将数据写入的表格页数
     * @author yanglixin
     */
    public static <T> void createLocalFile(List<T> data, ExcelWriter excelWriter, int pageNum) {
        WriteSheet writeSheet = EasyExcel.writerSheet(pageNum - 1, "第" + pageNum + "页").build();
        log.info("导出第{}页数据",pageNum);
        excelWriter.write(data,writeSheet);
        log.info("导出第{}页数据完成",pageNum);
    }

    /**
     * 获得excel文件的输入流
     * @author yanglixin
     */
    public static InputStream getFileImportStream(MultipartFile file) {
        //1.获得传入文件的输入流
        InputStream inputStream;
        try {
            inputStream = file.getInputStream();
        } catch (IOException e) {
            throw new RuntimeException("Excel文件读取失败!");
        }
        return inputStream;
    }

    /**
     * 使用模板导出
     *
     * @author yanglixin2
     * @param response http响应
     * @param templateFileName 类路径下的模板文件名
     * @param fileName 导出的文件名
     * @param dataClass 导出的数据分装类的类型
     * @param excelData 导出的数据
     */
    public static <T> void useTemplateExport(HttpServletResponse response, String templateFileName, String fileName, Class<T> dataClass, List<T> excelData) {
        try {
            //1.读取表格模板
            ClassPathResource resource = new ClassPathResource(templateFileName);
            InputStream inputStream = resource.getInputStream();

            //2.防止文件名乱码
            String fileNameEncode = URLUtil.encode(fileName, CharsetUtil.CHARSET_UTF_8);

            //3.设置响应为excel表格文件
            response.setHeader("content-disposition", "attachment;filename=" + fileNameEncode + ".xlsx");
            //response.setContentType("multipart/form-data");
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            //4.使用模板导出                                                         模板      导出后表格名称
            EasyExcel.write(response.getOutputStream(), dataClass).withTemplate(inputStream).sheet().doFill(excelData);
        } catch (IOException e) {
            log.error(ExceptionUtil.getStacktrace(e));
            throw new RuntimeException("导出失败");
        }
    }

    /**
     * 不使用模板导出
     *
     * @author yanglixin2
     * @param response http响应
     * @param fileName 导出的文件名
     * @param dataClass 导出的数据分装类的类型
     * @param excelData 导出的数据
     */
    public static <T> void normalExport(HttpServletResponse response, String fileName, Class<T> dataClass, List<T> excelData) {
        try {
            //2.防止文件名乱码
            String fileNameEncode = URLUtil.encode(fileName, CharsetUtil.CHARSET_UTF_8);

            //3.设置响应为excel表格文件
            response.setHeader("content-disposition", "attachment;filename=" + fileNameEncode + ".xlsx");
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            //4.使用模板导出                                                         模板      导出后表格名称
            ExcelWriter excelWriter = EasyExcelFactory.write(response.getOutputStream(), dataClass).build();
            WriteSheet writeSheet = new WriteSheet();
            writeSheet.setSheetNo(0);
            writeSheet.setSheetName("统计后的数据");
            excelWriter.write(excelData, writeSheet);
            excelWriter.finish();
        } catch (IOException e) {
            log.error(ExceptionUtil.getStacktrace(e));
            throw new RuntimeException("导出失败");
        }
    }

    /**
     * 导出到本地
     *
     * @author yanglixin2
     * @param fileLocal 文件地址
     * @param dataClass 导出的数据分装类的类型
     * @param excelData 导出的数据
     */
    public static <T> void exportLocal(String fileLocal, Class<T> dataClass, List<T> excelData) {
        try {
            File file = new File(fileLocal);
            OutputStream outputStream = new FileOutputStream(file);

            //4.使用模板导出                                                         模板      导出后表格名称
            ExcelWriter excelWriter = EasyExcelFactory.write(outputStream, dataClass).build();
            WriteSheet writeSheet = new WriteSheet();
            writeSheet.setSheetNo(0);
            writeSheet.setSheetName("统计后的数据");
            excelWriter.write(excelData, writeSheet);
            excelWriter.finish();
        } catch (IOException e) {
            log.error(ExceptionUtil.getStacktrace(e));
            throw new RuntimeException("导出失败");
        }
    }

    /**
     * Excel导入数据
     *
     * @author yanglixin2
     * @param dataClass 接受数据的实体类的类
     * @param file 导入的Excel文件
     */
    public static <T> List<T> importByExcel(MultipartFile file, Class<T> dataClass) {
        //1.获得传入文件的输入流
        InputStream inputStream = getFileImportStream(file);
        //2.创建监听器
        ExcelListener<T> excelListener = new ExcelListener<>();
        //3.读取传入的excel文件
        EasyExcel.read(inputStream, dataClass,excelListener).sheet().doRead();
        //读取的全部内容
        return excelListener.getData();
    }

    /**
     * Excel导入数据
     *
     * @author yanglixin2
     * @param listener 指定的监听器
     * @param file 导入的Excel文件
     */
    public static void importByExcel(MultipartFile file, AnalysisEventListener<Map<Integer, String>> listener) {
        //1.获得传入文件的输入流
        InputStream inputStream = getFileImportStream(file);
        //2.读取传入的excel文件
        EasyExcel.read(inputStream, listener)
                .headRowNumber(2)//设置头行数
                .sheet()
                .doRead();
    }

    public static <T> List<T> importByExcel(MultipartFile file, Class<T> dataClass, String sheetName) {
        //1.获得传入文件的输入流
        InputStream inputStream = getFileImportStream(file);
        //2.创建监听器
        ExcelListener<T> excelListener = new ExcelListener<>();
        //3.读取传入的excel文件
        EasyExcel.read(inputStream, dataClass,excelListener).sheet(sheetName).doRead();
        //读取的全部内容
        return excelListener.getData();
    }

    /**
     * excel导入数据
     * @param listener 指定的监听器
     * @param rowNumber 指定excel头行数(跳过几行数据开始读)
     */
    public static <T> void importByExcel(MultipartFile file, Class<T> dataClass, AnalysisEventListener<T> listener, Integer rowNumber) {
        InputStream inputStream = getFileImportStream(file);
        EasyExcel.read(inputStream, dataClass, listener)
                .headRowNumber(rowNumber)//设置头行数
                .sheet()
                .doRead();
    }

    /**
     * 读取类路径下的Excel文件
     *
     * @author yanglixin2
     * @param dataClass 接受数据的实体类的类
     * @param fileName 类路径下的文件
     */
    public static <T> List<T> importClassPathResourceExcel(String fileName, Class<T> dataClass) {
        //1.读取表格模板
        ClassPathResource resource = new ClassPathResource(fileName);
        //1.获得传入文件的输入流
        InputStream inputStream;
        try {
            inputStream = resource.getInputStream();
        } catch (IOException e) {
            throw new RuntimeException("Excel文件读取失败!");
        }
        //2.创建监听器
        ExcelListener<T> excelListener = new ExcelListener<>();
        //3.读取传入的excel文件
        EasyExcel.read(inputStream, dataClass,excelListener).sheet().doRead();
        //读取的全部内容
        return excelListener.getData();
    }

    /**
     * 获取上传的文件名
     * @param module 模块 (workForm,account,provider,settle)
     * @param fileMark 文件唯一标识
     * @author yanglixin
     */
    public static String getFileUrl(String module, String fileMark) {
        String date = DateUtil.format(new Date(), "yyyy/MM/dd");
        return String.format(EXCEL_FILE_NAME,module,date,fileMark);
    }

    /**
     * 生成唯一文件标识
     * @author yanglixin
     */
    public static String getFileMark() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    /**
     * 获取文件的访问url
     * @author yanglixin
     */
    public static String getURL(String bucketName, String endpoint, String fileName) {
        return String.format(EXCEL_URL,bucketName,endpoint,fileName);
    }

    /**
     * 获取本地临时文件的路径
     * @param fileMark 文件的唯一标识
     * @author yanglixin
     */
    public static <T> String getLocalFilePath(Class<T> startClass, String fileMark) {
        String path = Objects.requireNonNull(startClass.getResource("/")).getPath();
        return path + fileMark + ".xlsx";
    }

    /**
     * 获取ExcelWriter
     * @param localFile ExcelWriter将数据写入本地文件的路径
     * @param excelModeClass excel模型对象的类型
     */
    public static <T> ExcelWriter getExcelWriter(String localFile, Class<T> excelModeClass) {
        return EasyExcel.write(localFile, excelModeClass)
                .registerWriteHandler(STYLE)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())//自适应列宽引用方式
                .build();
    }

    /**
     * 获取表格样式
     * @author yanglixin
     */
    public static HorizontalCellStyleStrategy getStyle() {
        //1.头部策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        //设置背景颜色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        //设置头字体
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short)13);
        headWriteFont.setBold(true);
        headWriteCellStyle.setWriteFont(headWriteFont);
        // 字体样式
        headWriteFont.setFontName("宋体");
        //设置头居中
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        //headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
        // 自动换行
        //headWriteCellStyle.setWrapped(true);
        headWriteCellStyle.setWrapped(false);
        // 垂直对齐方式
        headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        //2.内容策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        //设置 水平居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        WriteFont contentWriteFont = new WriteFont();
        // 内容字号
        contentWriteFont.setFontHeightInPoints((short) 12);
        // 字体样式
        contentWriteFont.setFontName("宋体");
        contentWriteCellStyle.setWriteFont(contentWriteFont);

        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }

    /**
     * 获取表格样式
     * @author yanglixin
     */
    public static HorizontalCellStyleStrategy getImportTemplateStyle() {
        //1.头部策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        //设置背景颜色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        //设置头字体
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short)13);
        headWriteFont.setBold(true);
        headWriteCellStyle.setWriteFont(headWriteFont);
        // 字体样式
        headWriteFont.setFontName("宋体");
        //设置头靠左
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
        // 自动换行
        headWriteCellStyle.setWrapped(true);
        // 垂直对齐方式
        headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        //2.内容策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        //设置 水平居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        WriteFont contentWriteFont = new WriteFont();
        // 内容字号
        contentWriteFont.setFontHeightInPoints((short) 12);
        // 字体样式
        contentWriteFont.setFontName("宋体");
        contentWriteCellStyle.setWriteFont(contentWriteFont);

        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }

    /**
     * 获取工单导入模板的列宽拦截器对象
     * @author yanglixin
     */
    public static CustomCellWeightWeightConfig getImportWeightWeightConfig() {
        return new CustomCellWeightWeightConfig();
    }

    /**
     * 过去设置导出信息为纯文本的拦截器
     * @author yanglixin
     */
    public static ExcelCellWriteHandler getTextHandler() {
        return new ExcelCellWriteHandler();
    }
}
